module fifo(/*AUTOARG*/
	       // Outputs
	       data_out, fifo_empty, fifo_full, data_out_valid, err,
	       // Inputs
	       data_in, data_in_valid, pop_fifo, clk, rst
	    );

   input [63:0] data_in;
   input        data_in_valid;
   input        pop_fifo;
   input        clk;
   input        rst;

   output [63:0] data_out;
   output        fifo_empty;
   output        fifo_full;
   output        data_out_valid;
   output        err;
      //your code here
    //  We define four stage to store 64-bit data
    //  u[3:0] with input d_bus and output q_bus
   
    wire    [63:0]  q_bus0,q_bus1,q_bus2,q_bus3;
    wire    [63:0]  d_bus0,d_bus1,d_bus2,d_bus3;
    wire    q0,q1,q2,q3,d0,d1,d2,d3,rst0,rst1,rst2,rst3;
    wire    high = 1;
    wire    low = 0;
    
    dff64   U0  (.q(q_bus0),.d(d_bus0),.clk(clk),.rst(rst));
    dff64   U1  (.q(q_bus1),.d(d_bus1),.clk(clk),.rst(rst));
    dff64   U2  (.q(q_bus2),.d(d_bus2),.clk(clk),.rst(rst));
    dff64   U3  (.q(q_bus3),.d(d_bus3),.clk(clk),.rst(rst));

    wire    push = data_in_valid & ~fifo_full;
    wire    pop = pop_fifo & ~fifo_empty;

    assign  d_bus0 = push?data_in:q_bus0;
    assign  d_bus1 = push?q_bus0:q_bus1;
    assign  d_bus2 = push?q_bus1:q_bus2;
    assign  d_bus3 = push?q_bus2:q_bus3;

    //  Define another four dffs to monitor whether the fifo stages are occuplied or empty
    dff     U4  (.q(q0),.d(d0),.clk(clk),.rst(rst));
    dff     U5  (.q(q1),.d(d1),.clk(clk),.rst(rst));
    dff     U6  (.q(q2),.d(d2),.clk(clk),.rst(rst));
    dff     U7  (.q(q3),.d(d3),.clk(clk),.rst(rst));

    assign {d0,d1,d2,d3} =  (~push & ~pop)?{q0,q1,q2,q3}:
                            (~push & pop)?{q1,q2,q3,low}: 
                            (push & ~pop)?{high,q0,q1,q2}:{q0,q1,q2,q3};
    //  Outputs
    assign  data_out =  q3?q_bus3:
                        q2?q_bus2:
                        q1?q_bus1:
                        q0?q_bus0:{64{data_out_valid}};

    assign  fifo_empty = (~q0)&(~q1)&(~q2)&(~q3);
    assign  fifo_full = q0&q1&q2&q3;
    assign  data_out_valid = pop_fifo & (~fifo_empty);

    endmodule
